package com.biboheart.demo.report.support.jpa;

import com.biboheart.brick.utils.CheckUtils;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate;
import org.springframework.data.jpa.domain.Specification;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class Specifications {
    /**
     * @param name 字段名
     * @param input 左值
     * @param operation 运算 {1: &(默认), 2: |, 3: ^}
     * @param compare 比较 null 时表示>0 否则表示=此值
     * @return 条件
     */
    public static <S> Specification<S> bitSpec(String name, Number input, Integer operation, Number compare) {
        return (root, query, cb) -> {
            List<Predicate> pres = new ArrayList<>();
            if (null != input && BigDecimal.ZERO.compareTo(new BigDecimal(String.valueOf(input))) != 0) {
                Integer type = CheckUtils.isEmpty(operation) ? 1 : operation;
                SQLFunEnum sqlFunEnum = SQLFunEnum.get(type);
                if (CheckUtils.isEmpty(sqlFunEnum)) {
                    pres.add(cb.equal(root.get(name), input));
                } else {
                    Expression<Number> expression = cb.function(
                            sqlFunEnum.getBitFun(),
                            Number.class,
                            cb.literal(input),
                            root.get(name)
                    );
                    if (null == compare) {
                        pres.add(cb.gt(expression, 0));
                    } else {
                        pres.add(cb.equal(expression, compare));
                    }
                }
            }
            if (CheckUtils.isEmpty(pres)) {
                return null;
            }
            return cb.and(JpaUtils.listPredicateToArray(pres));
        };
    }

    public static <S> Specification<S> timeSpec(String fieldName, Long start, Long end) {
        return (root, query, criteriaBuilder) -> {
            List<Predicate> pres = new ArrayList<>();
            if (CheckUtils.isEmpty(fieldName)) {
                return null;
            }
            if (null != start) {
                pres.add(criteriaBuilder.ge(root.get(fieldName), start));
            }
            if (null != end) {
                pres.add(criteriaBuilder.le(root.get(fieldName), end));
            }
            if (CheckUtils.isEmpty(pres)) {
                return null;
            }
            return criteriaBuilder.and(JpaUtils.listPredicateToArray(pres));
        };
    }

    public static <S> Specification<S> matchSpec(String match, Collection<String> names) {
        return (root, query, builder) -> {
            if (CheckUtils.isEmpty(match) || CheckUtils.isEmpty(names)) {
                return null;
            }
            List<Predicate> pres = new ArrayList<>();
            String pattern = "%" + match + "%";
            names.forEach(name -> {
                if (!CheckUtils.isEmpty(name)) {
                    pres.add(builder.like(root.get(name), pattern));
                }
            });
            if (CheckUtils.isEmpty(pres)) {
                return null;
            }
            return builder.or(JpaUtils.listPredicateToArray(pres));
        };
    }
}
